#include "zh_info_tool.h"

char Format_Buffer[Format_Buffer_SIZE];
/**
 * @brief     字符参数格式化(用法和printf一样,返回格式化的字符串)
 * @param 	  包含格式的字符串
 * @param	  要格式化的数据
 * @note	  Format_Buffer为格式化的缓存请静态分配缓存大小
 * @retval    格式化后的字符指针
**/
char* FormatToStr(const char* fmt,...)
{
	va_list ap;
	va_start(ap,fmt);
	vsprintf((char*)Format_Buffer,fmt,ap);
	va_end(ap);
	return Format_Buffer;
}

/*
C 库函数 char *strstr(const char *haystack, const char *needle) 
在字符串 haystack 中查找第一次出现字符串 needle 的位置，不包含终止符 '\0'。
*/

/**
  * @brief  将一个缓存中的字符串分割为多个子串
  * @param  pSt :	Sentence_t句柄
  * @param	str :	要分割的字符串缓存
  * @param	size:   字符串缓存的尺寸
  * @note   请注意该函数会读写字符串缓存，不能传入const char*
  * @note	请先使用St_CHECK(pSt)检查再调用该函数
  * @retval 解析出的数据个数
  */
uint8_t St_Split(Sentence_t *pSt, uint8_t *str, uint8_t Size)
{
	uint8_t *pstr = str, *pbuf = pSt->pBuf;
	uint8_t index = 0;
	if(pSt == NULL || str == NULL || Size == 0 || pSt->headPos < 0 || pSt->tailPos < 0) return 0;
	str[pSt->tailPos] = '\0';//帧尾改成结束符
	pstr += pSt->headPos + pSt->headSize;//偏移到第一个数据的位置
	if(*pstr != pSt->separator)
		pbuf[index++] = (pstr - str);//记录第一个数据
	else goto reCheck;
	while(pSt->tailPos > (pstr - str))
	{
		if(*pstr == pSt->separator)//检测分隔符检测
		{
			reCheck:
			*pstr = '\0';//把分隔符换为结束符
			pstr++;
			if(*pstr == pSt->separator) goto reCheck;
			else if(index < pSt->BufSize && pSt->tailPos > (pstr - str))//缓存没满
			{
				pbuf[index++] = (pstr - str);
				// printf("Split index = %d\n",pstr - str);
			}
		}
		pstr++;
	}
	pSt->BufUsage = index;//将分隔符的数量存起来
	// printf("Split end Usage = %d\n",pSt->BufUsage);
	return index;//结束匹配

}


/**
 * @brief     设置待分析数据的地址
 * @param 	  Sentence句柄
 * @param 	  首地址
 * @param	  尺寸
 * @note 	  需要检测用Split解析多个地址时使用该函数更
 * 			  改分析的地址。切换后当前结果丢失需要重新分析
 * @retval    None
**/
void St_SetAddress(Sentence_t *pSt, uint8_t* Address, uint8_t Size)
{
	if(Address == NULL || Size == 0) return;
	pSt->pSrcAdr = Address;
	pSt->SrcAdrSize = Size;
	memset(pSt->pBuf, 0 , pSt->BufSize);
	pSt->BufUsage = 0;
}

/**
 * @brief     在缓存中查找数据段的位置
 * @param 	  pdata	查找的缓存
 * @param 	  Size	缓存长度
 * @param 	  AimData	搜索的字节串目标
 * @param 	  AimSize	目标的长度
 * @retval    [>=0:帧头偏移值][-1:不存在][-2:参数错误]
**/
int St_MemSearch(uint8_t* pdata, uint8_t Size, uint8_t* AimData, uint8_t AimSize)
{
	uint8_t* pch;
	uint8_t* pbuf = pdata;
	if(pdata == NULL || Size == 0 || AimData == NULL || AimSize == 0)return -2;

	while((pbuf < (pdata + Size)))
	{
		pch = (uint8_t*)memchr((char*)pbuf, *AimData, Size - (pbuf - pdata));
		// printf("pch: %c\n", *pch);
		if(pch == NULL)return -1;//没有找到帧头第一个字节
		
		if(AimSize == 1 || memcmp(pch, AimData, AimSize) == 0)break;//匹配帧头成功
		else
			pbuf = pch + 1;//迭代继续查找帧头
		
	}
	// printf("Search end pbuf: %c\n", *pch);
	return pch - pdata;
}


/*---------Example------------
解析下面的命令：
"PID:1,P,2.1.."	//设置PID1的P值

"PID:2,I,0.03.."//设置PID2的I值

"PID:3,D,0.2.."	//设置PID3的D值

"PID:4,a,2.1,0.03,0.2.."//同时设置PID4的P、I、D值

"PID:1,GetPID.."	//获取当前的PID1的P、I、D值

"PID:2,GetOut.."	//获取当前PID2的输出值

1.实例化对象
//定义全局变量
char UART1_RBuf[128] = {0};//串口接收缓存
char PIDSt_Buf[10] = {0};//解析器的缓存,Split分割后的数据的引索存储到这里
//创建全局的St对象并初始化
Sentence_t PIDSt = StCreate("PID:", ',', "..", PIDSt_Buf, UART1_RBuf);
	
2.根据需求编写命令应用代码
void PID_Ctrl_CMD(PID_t* pid)//PID_t为PID的句柄
{
	uint8_t* pstr = St_GetStr(&PIDSt,1);
	switch (*pstr)
	{
	case 'p'://设置P的值
		PID_SET_P(pid, St_GetDob(&PIDSt,2));
		break;
	case 'i'://设置I的值
		PID_SET_I(pid, St_GetDob(&PIDSt,2));
		break;
	case 'd'://设置D的值
		PID_SET_D(pid, St_GetDob(&PIDSt,2));
		break;
	case 'a'://同时设置PID
		PID_SET_P(pid, St_GetDob(&PIDSt,2));
		PID_SET_I(pid, St_GetDob(&PIDSt,3));
		PID_SET_D(pid, St_GetDob(&PIDSt,4));
		break;
	case 'G':
	if(strcmp(pstr,"GetPID") == 0)//获取当前P、I、D值
		printf("pid:%f,%f,%f\n\r", pid.p, pid.i, pid.d);
	if(strcmp(pstr,"GetOut") == 0)//获取PID输出值
		printf("pid:%f\n\r", pid.out);
		break;
	}
}

3.在串口处理任务中添加
void UART1_Handle()
{
	if(UART1_RFlag == 1)//在串口接收中断中置位接收标志,然后在任务中处理
	{
		UART1_RFlag = 0;//清空标志位
		if(St_SPLIT(&PIDSt))//检测是否有帧头帧尾,有则自动解析
		{
			switch(St_GetInt(&PIDSt,0))//第一个参数用来判断控制的对象
			{
				case 1:
					PID_Ctrl_CMD(&pid1);Beep(50);break;
				case 2:
					PID_Ctrl_CMD(&pid2);Beep(50);break;
				case 3:
					PID_Ctrl_CMD(&pid3);Beep(50);break;
				case 4:
					PID_Ctrl_CMD(&pid4);Beep(50);break;
			}
		}
	}
}
-----------------------------------------------*/




